// Tencent is pleased to support the open source community by making
// 蓝鲸智云 - 监控平台/日志平台 (BlueKing - Monitor/Log) available.
// Copyright (C) 2017-2022 THL A29 Limited, a Tencent company. All rights reserved.
// Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://opensource.org/licenses/MIT
// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
//

package otlp

import (
	"bytes"
	"net/http"

	"github.com/gogo/protobuf/jsonpb"
	"github.com/gogo/protobuf/proto"
	"go.opentelemetry.io/collector/model/otlpgrpc"
	spb "google.golang.org/genproto/googleapis/rpc/status"

	"github.com/TencentBlueKing/bk-collector/define"
	"github.com/TencentBlueKing/bk-collector/receiver"
)

const (
	Source = "otlp"

	routeV1Traces  = "/v1/traces"
	routeV1Trace   = "/v1/trace"
	routeV1Metrics = "/v1/metrics"
	routeV1Logs    = "/v1/logs"
)

func init() {
	receiver.RegisterHttpRoute(Source, Encoders, []receiver.RouteWithFunc{
		{
			Method:       http.MethodPost,
			RelativePath: routeV1Traces,
			HandlerFunc:  httpsrv.ExportTraces,
		},
		{
			Method:       http.MethodPost,
			RelativePath: routeV1Trace,
			HandlerFunc:  httpsrv.ExportTraces,
		},
		{
			Method:       http.MethodPost,
			RelativePath: routeV1Metrics,
			HandlerFunc:  httpsrv.ExportMetrics,
		},
		{
			Method:       http.MethodPost,
			RelativePath: routeV1Logs,
			HandlerFunc:  httpsrv.ExportLogs,
		},
	})
}

// Http Server

type httpSrv struct{}

var httpsrv httpSrv

func (s httpSrv) export(w http.ResponseWriter, rh receiver.ResponseHandler, req *http.Request, rtype define.RecordType) {
	receiver.HttpExport(Source, w, rh, req, rtype)
}

func (s *httpSrv) ExportTraces(w http.ResponseWriter, req *http.Request) {
	responseHandler := s.getResponseHandler(req.Header.Get(receiver.ContentType))
	s.export(w, responseHandler, req, define.RecordTraces)
}

func (s *httpSrv) ExportMetrics(w http.ResponseWriter, req *http.Request) {
	responseHandler := s.getResponseHandler(req.Header.Get(receiver.ContentType))
	s.export(w, responseHandler, req, define.RecordMetrics)
}

func (s *httpSrv) ExportLogs(w http.ResponseWriter, req *http.Request) {
	responseHandler := s.getResponseHandler(req.Header.Get(receiver.ContentType))
	s.export(w, responseHandler, req, define.RecordLogs)
}

func (s *httpSrv) getResponseHandler(contentType string) receiver.ResponseHandler {
	switch contentType {
	case receiver.ContentTypeProtobuf:
		return HttpPbResponseHandler()
	}
	// 缺省解析器为 contentTypeJson
	return HttpJsonResponseHandler()
}

func GetResponse(rtype define.RecordType) ([]byte, error) {
	switch rtype {
	case define.RecordTraces:
		return otlpgrpc.NewTracesResponse().Marshal()
	case define.RecordMetrics:
		return otlpgrpc.NewMetricsResponse().Marshal()
	case define.RecordLogs:
		return otlpgrpc.NewLogsResponse().Marshal()
	}
	return nil, define.ErrUnknownRecordType
}

// HttpPb Response Handler

func HttpPbResponseHandler() receiver.ResponseHandler {
	return httpPbResponseHandler{
		encoder: PbEncoder(),
	}
}

type httpPbResponseHandler struct {
	encoder receiver.Encoder
}

func (h httpPbResponseHandler) ContentType() string {
	return receiver.ContentTypeProtobuf
}

func (h httpPbResponseHandler) Response(rtype define.RecordType) ([]byte, error) {
	return GetResponse(rtype)
}

func (h httpPbResponseHandler) Unmarshal(rtype define.RecordType, b []byte) (define.RecordData, error) {
	return receiver.GetUnmarshal(h.encoder, rtype, b)
}

func (h httpPbResponseHandler) WriteResponse(w http.ResponseWriter, msg []byte) {
	receiver.WriteResponse(w, h.ContentType(), http.StatusOK, msg)
}

func (h httpPbResponseHandler) WriteError(w http.ResponseWriter, err error, statusCode int) {
	receiver.WriteError(w, h, err, statusCode)
}

func (h httpPbResponseHandler) MarshalErrorStatus(status interface{}) ([]byte, error) {
	buf := new(bytes.Buffer)
	s, ok := status.(*spb.Status)
	if !ok {
		return buf.Bytes(), nil
	}
	return proto.Marshal(s)
}

// HttpJson Response Handler

func HttpJsonResponseHandler() receiver.ResponseHandler {
	return httpJsonResponseHandler{
		marshaler: &jsonpb.Marshaler{},
		encoder:   JsonEncoder(),
	}
}

type httpJsonResponseHandler struct {
	marshaler *jsonpb.Marshaler
	encoder   receiver.Encoder
}

func (h httpJsonResponseHandler) ContentType() string {
	return receiver.ContentTypeJson
}

func (h httpJsonResponseHandler) Response(rtype define.RecordType) ([]byte, error) {
	return GetResponse(rtype)
}

func (h httpJsonResponseHandler) Unmarshal(rtype define.RecordType, b []byte) (define.RecordData, error) {
	return receiver.GetUnmarshal(h.encoder, rtype, b)
}

func (h httpJsonResponseHandler) WriteResponse(w http.ResponseWriter, msg []byte) {
	receiver.WriteResponse(w, h.ContentType(), http.StatusOK, msg)
}

func (h httpJsonResponseHandler) WriteError(w http.ResponseWriter, err error, statusCode int) {
	receiver.WriteError(w, h, err, statusCode)
}

func (h httpJsonResponseHandler) MarshalErrorStatus(status interface{}) ([]byte, error) {
	buf := new(bytes.Buffer)
	s, ok := status.(*spb.Status)
	if !ok {
		return buf.Bytes(), nil
	}
	err := h.marshaler.Marshal(buf, s)
	return buf.Bytes(), err
}
